package com.lemon.file.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.lemon.common.core.constant.CacheConstants;
import com.lemon.common.core.constant.ConfigConstants;
import com.lemon.common.core.constant.Constants;
import com.lemon.common.core.domain.dto.UploadImageConfigDto;
import com.lemon.common.core.exception.ServiceException;
import com.lemon.common.core.text.StrFormatter;
import com.lemon.common.core.utils.StringUtils;
import com.lemon.common.core.utils.primarykey.PrimaryKeyUtils;
import com.lemon.common.redis.service.RedisService;
import com.lemon.common.security.utils.OptEntityUtils;
import com.lemon.common.security.utils.SecurityUtils;
import com.lemon.file.config.MinioConfig;
import com.lemon.file.domain.BiElements;
import com.lemon.file.domain.BiFileInfo;
import com.lemon.file.domain.vo.FolderVo;
import com.lemon.file.mapper.BiElementsMapper;
import com.lemon.file.mapper.BiFileInfoMapper;
import com.lemon.file.service.ISysFileService;
import com.lemon.common.core.utils.file.FileUploadUtils;
import com.lemon.file.utils.MinioClientUtils;
import com.lemon.system.api.domain.SysFile;
import io.minio.messages.DeleteObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * Minio 文件存储
 * 使用注解@Primary 指定上传文件方式
 * @author huangpeng
 */
@Primary
@Service
public class MinioSysFileServiceImpl implements ISysFileService
{
    public static final Logger logger = LoggerFactory.getLogger(MinioSysFileServiceImpl.class);
    
    @Autowired
    private BiFileInfoMapper biFileInfoMapper;

    @Autowired
    private BiElementsMapper biElementsMapper;
    
    @Autowired
    private MinioClientUtils minioClientUtils;

    @Autowired
    private RedisService redisService;

    @Value("${web.file.totalSizeOneDay}")
    private String totalSizeOneDay;

    /**
     * minio文件上传接口
     * @Title: uploadFile
     * @Description: minio文件上传接口
     * @param path 自定义的文件夹名，为空则随机生成
     * @param file 上传的文件
     * @param customFileName 自定义的文件名，为空则随机生成
     * @return 访问地址,文件id，文件名
     * @throws:
     * @author: huangpeng
     * @Date: 2022/6/4 22:58
     */
    @Override
    public SysFile uploadFile(String path, MultipartFile file, String customFileName) throws Exception {
        MinioConfig minioConfig = getMinioConfig();
        String minioFileName = FileUploadUtils.getRandomFilename(file);
        String objectName = StringUtils.isEmpty(path) ? FileUploadUtils.getRandomPath().concat("/").concat(minioFileName) :
                path.concat("/").concat(minioFileName);
        String fileName = StringUtils.isEmpty(customFileName) ? FileUploadUtils.getFullFileName(file) : customFileName;
        String contentType = file.getContentType();
        UploadImageConfigDto uploadImageConfigDto = getUploadImageConfigDto();
        if (StringUtils.isNotEmpty(uploadImageConfigDto.getImageSuffix())
                && Arrays.asList(StringUtils.split(uploadImageConfigDto.getImageSuffix().toLowerCase(), Constants.STR_COMMA)).contains(FileUploadUtils.getExtension(file).toLowerCase())) {
            file = FileUploadUtils.compressPic(file, fileName, uploadImageConfigDto);
        }
        boolean result = minioClientUtils.putObject(minioConfig.getBucketName(), objectName, file.getInputStream(), contentType);
        if (result) {
            String fileUrl = minioClientUtils.getObjectUrl(minioConfig.getBucketName(), objectName);
            if (StringUtils.isEmpty(fileUrl)) {
                throw new ServiceException("文件上传失败！", null);
            }
            path = StringUtils.isEmpty(path) ? "/" : path.startsWith("/") ? path: "/" + path;
            BiFileInfo fileInfo = new BiFileInfo();
            fileInfo.setFileExtName(FileUploadUtils.getExtension(file));
            fileInfo.setFileName(fileName);
            fileInfo.setFileSize(String.valueOf(file.getSize()));
            fileInfo.setFolder(path);
            fileInfo.setStoragePlace(Constants.FILE_STORAGE_PLACE_MINIO);
            fileInfo.setAdminId(SecurityUtils.getUserId().toString());
            fileInfo.setUserId(SecurityUtils.getLoginUser().getUserid().toString());
            fileInfo.setFileUrl(fileUrl);
            fileInfo.setStatus(Constants.LOGIN_SUCCESS_STATUS);
            fileInfo.setId(PrimaryKeyUtils.generateId());
            OptEntityUtils.preInsert(fileInfo);
            biFileInfoMapper.insertSelective(fileInfo);
            SysFile sysFile = new SysFile();
            sysFile.setFileId(fileInfo.getId());
            sysFile.setName(fileName);
            sysFile.setUrl(fileUrl);
            return sysFile;
        } else {
            throw new ServiceException("文件上传失败！", null);
        }

    }

    @Override
    public void download(String fileName, String originFileName, HttpServletResponse response) throws Exception {
        MinioConfig minioConfig = getMinioConfig();
        minioClientUtils.downloadFile(minioConfig.getBucketName(), fileName, originFileName, response);
    }

    @Override
    public FolderVo listAll() {
        MinioConfig minioConfig = getMinioConfig();
        return minioClientUtils.listAll(minioConfig.getBucketName());
    }

    @Override
    public boolean deleteSourceFileByBatch(List<String> filenameList) throws Exception {
        MinioConfig minioConfig = getMinioConfig();
        List<DeleteObject> deleteObjects = new ArrayList<>(filenameList.size());
        for (String filename : filenameList) {
            deleteObjects.add(new DeleteObject(filename));
        }
        List<String> errorList = minioClientUtils.removeObjects(minioConfig.getBucketName(), deleteObjects);
        return CollUtil.isEmpty(errorList);
    }

    @Override
    public List<BiFileInfo> selectBiFileInfoList(BiFileInfo biFileInfo) {
        return biFileInfoMapper.selectBiFileInfoList(biFileInfo);
    }

    @Override
    public int deleteBiFileInfoByIds(Long[] ids) throws Exception {
        return biFileInfoMapper.deleteByIds(ids);
    }

    @Override
    public List<BiFileInfo> selectFileInfoByIds(Long[] ids) {
        return biFileInfoMapper.selectByIds(ids);
    }

    @Override
    public SysFile uploadFileWeb(String path, MultipartFile file, String fileName) throws Exception {
        Long currUserId = null;
        if (StrUtil.isNotEmpty(SecurityUtils.getUsername())) {
            currUserId = SecurityUtils.getLoginUser().getUserid();
        }
        if (currUserId == null) {
            throw new ServiceException("当前用户未登录！");
        }
        // 判断当前用户上传文件大小是否超过20m
        List<BiFileInfo> fileList = biFileInfoMapper.selectByUserId(String.valueOf(currUserId));
        long fileSizeTotal = 0L;
        for (BiFileInfo fileInfo : fileList) {
            if (StrUtil.isNumeric(fileInfo.getFileSize())) {
                fileSizeTotal += Long.parseLong(fileInfo.getFileSize());
            }
        }
        logger.info("获取到的文件大小, fileSizeTotal={}", fileSizeTotal);
        if (StrUtil.isNumeric(totalSizeOneDay) && Long.parseLong(totalSizeOneDay) < fileSizeTotal) {
            throw new ServiceException("当天允许的上传文件总大小已超出限额，日限额" + StrFormatter.formatFileSize(fileSizeTotal));
        }
        return uploadFile(path, file, fileName);
    }

    @Override
    public List<BiFileInfo> listWed(BiFileInfo biFileInfo) {
        Long currUserId = null;
        if (StrUtil.isNotEmpty(SecurityUtils.getUsername())) {
            currUserId = SecurityUtils.getLoginUser().getUserid();
        }
        biFileInfo.setUserId(String.valueOf(currUserId));
        return biFileInfoMapper.selectBiFileInfoList(biFileInfo);
    }

    /**
     * 获取minio配置信息
     * @Title: getMinioConfig
     * @Description: 获取minio配置信息
     * @return: MinioConfig
     * @throws:
     * @author: huangpeng
     * @Date: 2023/4/10 20:58
     */
    private MinioConfig getMinioConfig() {
        MinioConfig minioConfig;
        try {
            Map<String, Object> configMap = new HashMap<>();
            String cacheKey = ConfigConstants.MINIO_CONFIG.toLowerCase() + CacheConstants.KEY_SUFFIX;
            if (redisService.hasKey(cacheKey)) {
                configMap = redisService.getCacheMap(cacheKey);
            } else {
                BiElements element = new BiElements();
                element.setFunctionCode(ConfigConstants.MINIO_CONFIG);
                List<BiElements> elementsList = biElementsMapper.selectBiElementsList(element);
                for (BiElements elementInfo : elementsList) {
                    configMap.put(elementInfo.getElementCode(), elementInfo.getElementValue());
                }
                redisService.setCacheMap(cacheKey, configMap);
            }
            minioConfig = JSONObject.parseObject(JSONObject.toJSONString(configMap), MinioConfig.class);
        } catch (Exception e) {
            throw new ServiceException("获取minio配置信息出错，请检查是否在要素配置中配置minio的配置信息");
        }
        return minioConfig;
    }

    /**
     * 获取图片上传配置信息
     * @Title: getUploadImageConfigDto
     * @Description: 获取图片上传配置信息
     * @return: UploadImageConfigDto
     * @throws:
     * @author: huangpeng
     * @Date: 2024/4/12 14:42
     */
    private UploadImageConfigDto getUploadImageConfigDto() {
        UploadImageConfigDto uploadImageConfigDto;
        try {
            Map<String, Object> configMap = new HashMap<>();
            String cacheKey = ConfigConstants.UPLOAD_IMAGE_CONFIG.toLowerCase() + CacheConstants.KEY_SUFFIX;
            if (redisService.hasKey(cacheKey)) {
                configMap = redisService.getCacheMap(cacheKey);
            } else {
                BiElements element = new BiElements();
                element.setFunctionCode(ConfigConstants.UPLOAD_IMAGE_CONFIG);
                List<BiElements> elementsList = biElementsMapper.selectBiElementsList(element);
                for (BiElements elementInfo : elementsList) {
                    configMap.put(elementInfo.getElementCode(), elementInfo.getElementValue());
                }
                redisService.setCacheMap(cacheKey, configMap);
            }
            uploadImageConfigDto = JSONObject.parseObject(JSONObject.toJSONString(configMap), UploadImageConfigDto.class);
        } catch (Exception e) {
            throw new ServiceException("获取minio配置信息出错，请检查是否在要素配置中配置minio的配置信息");
        }
        return uploadImageConfigDto;
    }
}
